#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>
#include <sys/time.h>

#define NUMDIGITOS 10000001
#define NUMITERACOES 100000000
#define PRECISAO_NUMERO_ALEATORIO 1000
#define LOG_2_10 3.32192809488736234787


int main(void)
{

    // <editor-fold defaultstate="collapsed" desc="Inicia Contagem de Tempo">
    struct timeval tvInitial, tvFinal;
    double tInitial, tFinal;
    gettimeofday(&tvInitial, NULL);
    tInitial = (double) (tvInitial.tv_sec) + (double) (tvInitial.tv_usec) / 1000000.00;
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Declaracao das Variaveis">
    int pontosTotal;
    mpf_t x, y, z, pontosDentro, pi;
    gmp_randstate_t numeroAleatorio;

    // Precisão
    mpf_set_default_prec(LOG_2_10 * NUMDIGITOS);

    mpf_inits(x, y, z, pontosDentro, pi, NULL);
    gmp_randinit_default(numeroAleatorio);
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Metodo de Monte Carlo">
    for (pontosTotal = 0; pontosTotal < NUMITERACOES; pontosTotal++) {
        // X = rand[0,1]
        mpf_urandomb(x, numeroAleatorio, PRECISAO_NUMERO_ALEATORIO);

        // Y = rand[0,1]
        mpf_urandomb(y, numeroAleatorio, PRECISAO_NUMERO_ALEATORIO);

        // X = X^2
        mpf_pow_ui(x, x, 2);

        // Y = Y^2
        mpf_pow_ui(y, y, 2);

        // Z = X + Y
        mpf_add(z, x, y);

        // Se Z < 1, pontosDentro++
        if (mpf_cmp_d(z, 1.0) < 0.0) {
            // P(dentro) = P(dentro) + 1
            mpf_add_ui(pontosDentro, pontosDentro, 1);
        }
        
    }

    // PI = 4 * P(dentro) / P(total)
    mpf_div_ui(pi, pontosDentro, NUMITERACOES);
    mpf_mul_ui(pi, pi, 4);
    
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Impressao dos Resultados">
    gmp_printf("PI(%d) = %.6Ff\n", pontosTotal, pi);
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Desalocacao das Variaveis">
    mpf_clears(x, y, z, NULL);
    mpf_clear(pontosDentro);
    mpf_clear(pi);
    gmp_randclear(numeroAleatorio); // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Finaliza Contagem de Tempo">
    //fecha contagem de tempo
    gettimeofday(&tvFinal, NULL);
    tFinal = (double) (tvFinal.tv_sec) + (double) (tvFinal.tv_usec) / 1000000.00;
    // Imprime o tempo de execucao
    printf("Tempo de execucao: %.2lfs\n", (tFinal - tInitial)); // </editor-fold>

    // Retorno
    return EXIT_SUCCESS; // </editor-fold>

}
